/*  tBookShelf: TextPlugin
    (C) 2007  TvN (Volodymyr Tarasenko), e-mail: tvntsr@yahoo.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <time.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>

#include <iostream>
#include <vector>
#include <memory>

#include <hlapi.h>
#include <assert.h>

#include "TextPlugin.hpp"

/* :TODO: Implement and check next functions
Function HlPageRender

Function HlDocumentCreate

Function HlDocumentGetDocInfo
Function HlPageCreateByPageNo
Function HlDocumentGetLayoutInfo
Function HlDocumentGetDocInfo
Function HlPageSetRotation

Function HlPageGetPageInfo
*/

using namespace std;

// -----------------------------------------------------------
// IMPLEMENTATION
// -----------------------------------------------------------
HlPageS* HlDocumentS::getPage(int pageNo)
{
    std::auto_ptr<HlPageS> 
        page(new HlPageS(this, pageNo));

    return page.release();
}

int HlPageS::getPageText(unsigned char* text, int len)
{
    memset(text, 0, len);

    try{

        TextReader& txt = document->text_reader;

        std::string to;

        txt.getPageText(getPageNum(),
                        0, 
                        to,
                        len);
        
        memcpy(text, to.c_str(), len > to.size()? to.size():len);

        return Hl_OK;
    }catch(std::exception& err)
    {
        dout << __FUNCTION__
             << ": ERROR:"
             << err.what()
             << endl;
    }

    return Hl_BAD;
}


// -----------------------------------------------------------
// Parser Info
// -----------------------------------------------------------
HlStatus HlGetParserInfo(HlParserInfo* parserInfo)
{
    TRACE;
    assert(parserInfo);

    parserInfo->parserName       = PLUGIN_PARSER_NAME;
    parserInfo->parserVersion    = PLUGIN_PARSER_VER;
    parserInfo->interfaceVersion = PLUGIN_INTERF_VER;

    return Hl_OK;
}

/* Document */
HlDocument* HlDocumentCreate(const char* fileName, 
                             HlRect*     screenRect, 
                             int         lang)
{
    TRACE;

    assert(fileName);
    HlDocument *document = 0;

    try
    {
        char *main_cfg_file = getenv("TextReaderCfg");
        if(!main_cfg_file)
        {
            main_cfg_file = "/etc/textplugin.lua";
        }

        auto_ptr<HyphenationManager> hyph_man(
            new HyphenationManager());

        auto_ptr<ConfigParser> config(
            new ConfigParser(main_cfg_file, 
                             fileName,
                             hyph_man.get()));

        initDebug(config.get());
       
        auto_ptr<FontManager> font_man(
            new FontManager());

        font_man->setHyphenationManager(hyph_man.get());

        dout << "Create doc: file " << fileName << endl;

        auto_ptr<HlDocument> 
            doc(new HlDocument(fileName, *screenRect, lang));

        doc->setDocType(config->getDocType());

        doc->layout_info.fontSize       = config->getFontSize();
        doc->layout_info.interwordSpace = config->getInterWordSpace();
        doc->layout_info.interlineSpace = config->getInterLineSpace();
        doc->layout_info.brightness     = config->getBrightness();
        doc->layout_info.contrast       = config->getContrast();

        int max_page_cache = config->getPageCacheSize();
        if (max_page_cache == 0)
        {
            max_page_cache = DEFAULT_MAX_PAGE_CACHE;
        }

        TextReader* txt_reader = &doc->text_reader;
        
        txt_reader->registerConfigManager(config);
        txt_reader->registerFontManager(font_man);
        txt_reader->registerHyphenationManager(hyph_man);

        auto_ptr<PageCacheManager> page_man(
            new PageCacheManager(max_page_cache));

        txt_reader->registerCacheManager(page_man);

        auto_ptr<StreamManager> smanager(
            new StreamManager);
        
        txt_reader->registerStreamManager(smanager);

        dout << "fileName:"
             << fileName
             << ", screen ("
             << screenRect->x  << ", "<< screenRect->y
             << " x " 
             << screenRect->w  << ", "<< screenRect->h
             << "), lang:"
             << lang
             << endl;

        txt_reader->setPageOrientation(Portrait);


        dout << "Open file" << endl;

        doc->doc_info.pageNum = txt_reader->open(fileName);

        dout << "Open successsful" << endl;

        document = doc.release();
    }catch(exception& exc)
    {
        dout << __FUNCTION__ << ":Error " << exc.what()
             << endl;
    }
    catch(...)
    {
        dout << __FUNCTION__ << ": Unknown exception" << endl;
    }

    return document;
}

void HlDocumentRelease(HlDocument* document)
{
    TRACE;
    assert(document);

    delete document;
}

HlStatus HlDocumentGetDocInfo(HlDocument* document, HlDocInfo *docInfo)
{
    TRACE;
    assert(document);
    assert(docInfo);

    return document->getDocInfo(docInfo);
}

HlStatus HlDocumentSetLocalLanguage(HlDocument* document, int lang)
{
    TRACE;
    assert(document);

    document->setLang(lang);

    return Hl_OK;
}

HlStatus HlDocumentGetLayoutInfo(HlDocument*   document, 
                                 HlLayoutInfo* layoutInfo)
{
    TRACE;
    assert(document);
    assert(layoutInfo);

    return document->getLayout(layoutInfo);
}

HlStatus HlDocumentSetLayoutInfo(HlDocument*   doc, 
                                 HlLayoutInfo* layoutInfo)
{
    TRACE;
    assert(doc);
    assert(layoutInfo);

    dout << "Current values:"
         << "doc->layout_info.fontSize:" << doc->layout_info.fontSize
         << ", doc->layout_info.interwordSpace:" << doc->layout_info.interwordSpace
         << ", doc->layout_info.interlineSpace:" << doc->layout_info.interlineSpace
         << ", doc->layout_info.brightness:"     << doc->layout_info.brightness
         << ", doc->layout_info.contrast:"       << doc->layout_info.contrast
         << endl
         << "New values:"
         << "doc->layout_info.fontSize:"         << layoutInfo->fontSize
         << ", doc->layout_info.interwordSpace:" << layoutInfo->interwordSpace
         << ", doc->layout_info.interlineSpace:" << layoutInfo->interlineSpace
         << ", doc->layout_info.brightness:"     << layoutInfo->brightness
         << ", doc->layout_info.contrast:"       << layoutInfo->contrast
         << endl;

    return doc->setLayout(layoutInfo);
}


HlPage* HlPageCreateByPageNo(HlDocument* document, int pageNo)
{
    TRACE;
    dout << "Get Page " << pageNo << endl;

    assert(document);

    return document->getPage(pageNo);
}

/*
 * create page by page ID
 * pageId is a string, we used it in outline and bookmark.
 */
HlPage* HlPageCreateByPageId(HlDocument* document, unsigned char *pageId)
{
    TRACE;
    assert(document);
    assert(pageId);

    int page_no = 0;

    HlGetPageNoFromPageId(document, pageId, &page_no);

    return HlPageCreateByPageNo(document, page_no);
}

/* Release Page */
void HlPageRelease(HlPage* page)
{
    TRACE;
    assert(page);

    delete page;
}

/* Get page id */
unsigned char *HlPageGetPageId(HlPage* page)
{
    TRACE;
    assert(page);

    return page->page_info.pageId;
}

/* Set Page Rotation , new added */
HlStatus HlPageSetRotation(HlPage* page, HlPageRotation rotation)
{
    TRACE;
    assert(page);

    cerr << "HlDocRotate_0:"     << HlDocRotate_0
         << ", HlDocRotate_90:"  << HlDocRotate_90
         << ", HlDocRotate_180:" << HlDocRotate_180
         << ", HlDocRotate_270:" << HlDocRotate_270
         << endl;
    dout << "Previous orientation is "
         << page->page_info.rotation
         << ", new one is "
         << rotation
         << endl;

    cerr << "Previous orientation is "
         << page->page_info.rotation
         << ", new one is "
         << rotation
         << endl;

    if (page->page_info.rotation == rotation)
        return Hl_OK;

    page->page_info.rotation = rotation;

    PageOrientation orient;
    switch(rotation)
    {
        case HlDocRotate_0:
        case HlDocRotate_180:
            orient = Portrait;
            break;
        default:
            orient = Landscape;
    }

    page->document->text_reader.setPageOrientation(orient);

    page->document->doc_info.pageNum 
        = page->document->text_reader.getPageCount();
    return Hl_OK;
}

/* Get page Info */
HlStatus HlPageGetPageInfo(HlPage* page, HlPageInfo *pageInfo)
{
    TRACE;
    assert(page);
    assert(pageInfo);

    return page->getPageInfo(pageInfo);
}

/* Get Page text Length */
int HlPageGetPageTextLen(HlPage*page)
{
    TRACE;
    assert(page);
    
    return 0;
}

/* Get page text, the text is utf8 format */
HlStatus HlPageGetPageText(HlPage*page, unsigned char* text, int len)
{
    TRACE;
    assert(page);

    dout << "Page is " << page->getPageNum()
         << ", text buffer's len is " << len << endl;

    return page->getPageText(text, len);
}

/* Create the image buffer, if not support the format, return 0 */
HlStatus HlPageRender(HlPage*  page, 
                      HlRect*  pageRect, 
                      HlRect*  renderRect, 
                      HlFormat format, 
                      int      rowSize, 
                      char*    imageBuffer)
{
    TRACE;
    cerr << "Page:" << page->getPageNum() 
         << "Page rect: ("
         << pageRect->x  << ", "<< pageRect->y
         << ")x(" 
         << pageRect->w  << ", "<< pageRect->h
         << ")" << endl
         << "Rend rect: ("
         << renderRect->x  << ", "<< renderRect->y
         << ")x(" 
         << renderRect->w  << ", "<< renderRect->h
         << "), rowSize:"
         << rowSize
         << ", Format is " << format
         << endl;
    

    int ret = Hl_OK;

//    memset(imageBuffer, 0xFF, rowSize*renderRect->h);
    memset(imageBuffer, 0xFF, rowSize*renderRect->y);

    try{
        TextReader& text = page->document->text_reader;
        PageCacheManager& pman = text.cacheManager();

        PageSanity sanity(pman,
                          pman.getPage(page->getPageNum()));
        if (sanity.get() == 0)
        {
            dout << "Cannot create page" << endl;
            return Hl_BAD;
        }

        Point start(renderRect->x, renderRect->y);
        Point end  (renderRect->x + renderRect->w, 
                    renderRect->y + renderRect->h);

        text.renderArea(sanity.get(),
                        imageBuffer,
                        start,
                        end);
        
        dout << "Render done" << endl;
    }
    catch(std::exception &exc)
    {
        dout << "Render error:" << exc.what()
             << endl;
        ret = Hl_BAD;
    }

    return ret;
}

/* Outline(Directory) */
/* Create root outline */
HlOutline* HlDocumentGetRootOutline(HlDocument* document)
{
    TRACE;
    assert(document);

    return NULL;
}

/* You only need to release the root outline, the child outlines will be released also */
void HlRootOutlineRelease(HlOutline* /*outline*/)
{
    TRACE;

    return;
}

/* If outline is RootOutline, return NULL */
HlOutline* HlOutlineGetParentOutline(HlOutline* /*outline*/)
{
    TRACE;
    return NULL;
}


/* Child Outline num */
int HlOutlineGetChildNum(HlOutline* /*outline*/)
{
    TRACE;
    return 0;
}

/* If index is beyond child outline range, return NULL */
HlOutline* HlOutlineGetChildOutline(HlOutline* /*outline*/, int /*index*/)
{
    TRACE;
    return NULL;
}

/* Get outline name, the return name should be utf8 format */
unsigned char* HlOutlineGetName(HlOutline* /*outline*/)
{
    TRACE;
    return NULL;
}

/* Get outline pageId */
unsigned char* HlOutlineGetPageId(HlOutline* /*outline*/)
{
    TRACE;
    return NULL;
}

/* misc */
/* Translate between pageId and pageNo */
void HlGetPageNoFromPageId(HlDocument*    /*document*/,
                           unsigned char* pageId,
                           int*           pageNo)
{
    TRACE;
    assert(pageId);
    *pageNo = 0;
}
void HlGetPageIdFromPageNo(HlDocument*    /*document*/, 
                           int            /*pageNo*/, 
                           unsigned char* pageId)
{
    TRACE;
    assert(pageId);
}

/* Viewer can save the document specific data and restore 
   them at the next time open it
*/
int HlGetDocumentPrivateDataLen(HlDocument* /*document*/)
{
    TRACE;
    return 0;
}

HlStatus HlGetDocumentPrivateData(HlDocument*    /*document*/, 
                                  unsigned char* /*data*/,
                                  int            /*len*/)
{
    TRACE;
    return 0;
}

HlStatus HlSetDocumentPrivateData(HlDocument*    /*document*/, 
                                  unsigned char* /*data*/,
                                  int            /*len*/)
{
    TRACE;
    return 0;
}

int HlOnIdle()
{
    TRACE;
    return 0;
}
